﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Security.Cryptography;
using System.IO;
using System;
using System.Text;

public class Helper  {

	// 只支持 "utf-8" & "RSA2"
    public static string RSASign(string data, string privateKey) {
		RSACryptoServiceProvider rsaCsp = AssembleCertification (privateKey, "RSA2");

		byte[] dataBytes = null;

		dataBytes = Encoding.UTF8.GetBytes(data);

		byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA256");

		return Convert.ToBase64String(signatureBytes);
    }

	private static RSACryptoServiceProvider AssembleCertification(string privateKey, string signType)
	{
		byte[] res = null;
		//res = GetPem("RSA PRIVATE KEY", Encoding.UTF8.GetBytes(privateKey));
		res = GetPem(privateKey);

		try
		{
			RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(res, signType);
			return rsa;
		}
		catch (Exception ex)
		{
			throw ex;
		}
		return null;
	}

	private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey, string signType)
	{
		byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;

		// --------- Set up stream to decode the asn.1 encoded RSA private key ------
		MemoryStream mem = new MemoryStream(privkey);
		BinaryReader binr = new BinaryReader(mem);  //wrap Memory Stream with BinaryReader for easy reading
		byte bt = 0;
		ushort twobytes = 0;
		int elems = 0;
		try
		{
			twobytes = binr.ReadUInt16();
			if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
				binr.ReadByte();    //advance 1 byte
			else if (twobytes == 0x8230)
				binr.ReadInt16();    //advance 2 bytes
			else
				return null;

			twobytes = binr.ReadUInt16();
			if (twobytes != 0x0102) //version number
				return null;
			bt = binr.ReadByte();
			if (bt != 0x00)
				return null;


			//------ all private key components are Integer sequences ----
			elems = GetIntegerSize(binr);
			MODULUS = binr.ReadBytes(elems);

			elems = GetIntegerSize(binr);
			E = binr.ReadBytes(elems);

			elems = GetIntegerSize(binr);
			D = binr.ReadBytes(elems);

			elems = GetIntegerSize(binr);
			P = binr.ReadBytes(elems);

			elems = GetIntegerSize(binr);
			Q = binr.ReadBytes(elems);

			elems = GetIntegerSize(binr);
			DP = binr.ReadBytes(elems);

			elems = GetIntegerSize(binr);
			DQ = binr.ReadBytes(elems);

			elems = GetIntegerSize(binr);
			IQ = binr.ReadBytes(elems);


			// ------- create RSACryptoServiceProvider instance and initialize with public key -----
			CspParameters CspParameters = new CspParameters();
			CspParameters.Flags = CspProviderFlags.NoFlags;

			int bitLen = 1024;
			if ("RSA2".Equals(signType))
			{
				bitLen = 2048;
			}

			RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(bitLen, CspParameters);
			RSAParameters RSAparams = new RSAParameters();
			RSAparams.Modulus = MODULUS;
			RSAparams.Exponent = E;
			RSAparams.D = D;
			RSAparams.P = P;
			RSAparams.Q = Q;
			RSAparams.DP = DP;
			RSAparams.DQ = DQ;
			RSAparams.InverseQ = IQ;
			RSA.ImportParameters(RSAparams);
			return RSA;
		}
		catch (Exception ex)
		{
			throw ex;
		}
		finally
		{
			binr.Close();
		}
	}

	private static int GetIntegerSize(BinaryReader binr)
	{
		byte bt = 0;
		byte lowbyte = 0x00;
		byte highbyte = 0x00;
		int count = 0;
		bt = binr.ReadByte();
		if (bt != 0x02)		//expect integer
			return 0;
		bt = binr.ReadByte();

		if (bt == 0x81)
			count = binr.ReadByte();	// data size in next byte
		else
			if (bt == 0x82)
			{
				highbyte = binr.ReadByte();	// data size in next 2 bytes
				lowbyte = binr.ReadByte();
				byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
				count = BitConverter.ToInt32(modint, 0);
			}
			else
			{
				count = bt;		// we already have the data size
			}

		while (binr.ReadByte() == 0x00)
		{	//remove high order zeros in data
			count -= 1;
		}
		binr.BaseStream.Seek(-1, SeekOrigin.Current);		//last ReadByte wasn't a removed zero, so back up a byte
		return count;
	}

	private static byte[] GetPem(string privateKey)
	{
//		string pem = Encoding.UTF8.GetString(data);
//		string header = String.Format("-----BEGIN {0}-----\\n", type);
//		string footer = String.Format("-----END {0}-----", type);
//		int start = pem.IndexOf(header) + header.Length;
//		int end = pem.IndexOf(footer, start);
//		string base64 = pem.Substring(start, (end - start));

		return Convert.FromBase64String(privateKey);
	}

//	private static byte[] GetPem(string type, byte[] data)
//	{
//		string pem = Encoding.UTF8.GetString(data);
//		string header = String.Format("-----BEGIN {0}-----\\n", type);
//		string footer = String.Format("-----END {0}-----", type);
//		int start = pem.IndexOf(header) + header.Length;
//		int end = pem.IndexOf(footer, start);
//		string base64 = pem.Substring(start, (end - start));
//
//		return Convert.FromBase64String(base64);
//	}

}
